// 1. 增加异步状态下的链式调用
// 2. 增加回调函数执行结果的判断
// 3. 增加识别 Promise 是否返回自己
// 4. 增加错误捕获

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

/**
 *
 *
 * @class MyPromise
 */
class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)    
        } catch (error) {
            this.reject(error)   
        }
    }
    status = PENDING

    value = null
    reason = null

    onFulfilledCallbacks = []
    onRejectedCallbacks = []

    resolve = (value) => {
        if (this.status === PENDING) {
            this.status = FULFILLED
            this.value = value
            while(this.onFulfilledCallbacks.length) {
                this.onFulfilledCallbacks.shift()(value)
            }
        }
    }
    reject = (reason) => {
        if (this.status === PENDING) {
            this.status = REJECTED
            this.reason = reason
            while(this.onRejectedCallbacks.length) {
                this.onRejectedCallbacks.shift()(reason)
            }
        }
    }

    then = (onFulfilled, onRejected) => {
        const p2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value)
                        resolvePromise(p2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
                
            }
            else if (this.status === REJECTED) {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason)
                        resolvePromise(p2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } 
            else {
                this.onFulfilledCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value)
                            resolvePromise(p2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason)
                            resolvePromise(p2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
            }
        })
        return p2
    }
}
function resolvePromise(p2, x, resolve, reject) {
    if (x instanceof MyPromise) {
        if (x === p2) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        } else {
            x.then(resolve, reject)
        }
    } else {
        resolve(x)
    }
}
